function watch(ctx, obj) {
    Object.keys(obj).forEach(key => {
        defineReactive(ctx.data, key, ctx.data[key], function (value) {
            obj[key].call(ctx, value)
        })
    })
}

function computed(ctx, obj) {
    let keys = Object.keys(obj)
    let dataKeys = Object.keys(ctx.data)
    dataKeys.forEach(dataKey => {
        defineReactive(ctx.data, dataKey, ctx.data[dataKey])
    })
    let firstComputedObj = keys.reduce((prev, next) => {
        ctx.data.$target = function () {
            ctx.setData({
                [next]: obj[next].call(ctx)
            })
        }
        prev[next] = obj[next].call(ctx)
        ctx.data.$target = null
        return prev
    }, {})
    ctx.setData(firstComputedObj)
}

function defineReactive(data, key, val, fn) {
    let subs = data['$' + key] || []
    Object.defineProperty(data, key, {
        configurable: true,
        enumerable: true,
        get: function () {
            if (data.$target) {
                subs.push(data.$target)
                data['$' + key] = subs
            }
            return val
        },
        set: function (newVal) {
            if (newVal === val) return
            fn && fn(newVal)
            if (subs.length) {
                // 用 setTimeout 因为此时 this.data 还没更新
                setTimeout(() => {
                    subs.forEach(sub => sub())
                }, 0)
            }
            val = newVal
        },
    })
}

module.exports = {
    watch,
    computed
};